package main

import (
	"fmt"
	"sync"
	"time"
)

// 雪花算法
// +---------------------------------------------------------------+
// |  unused(1)  |  timestamp(41)  |  node id(10)  |  sequence(12)  |
// +---------------------------------------------------------------+

// ref: https://blog.csdn.net/Nassue_sn/article/details/109011883
// 调整之后的分布图
// +---------------------------------------------------------------+
// |  unused(1)  |  timestamp(32)  |  node id(6)  |  sequence(15)  |
// +---------------------------------------------------------------+

// ID生成时间精度降低
// 服务使用前期，生成ID长度稳定性较大，存在逐渐变长的情况，仅保持ID递增

//总结: 雪花算法满足大多数唯一ID生成场景，若存在特定的业务需求，可根据实际情况调整二进制数据位数分配。
//本文描述方案最终解决了Zset存储Score值精度问题，但也引发了新的问题，如ID长度变化较大，时间精度下降等，本文处理方案仅供参考，可根据业务场景灵活调整。

const (
	// 2020-08-18 00:00:00 可使用136年 单机每秒32768
	twepoch      = int64(1417937700000) // 默认起始的时间戳 1449473700000 。计算时，减去这个值
	NodeIdBits   = uint(6)              //节点 所占位置
	sequenceBits = uint(15)             //自增ID 所占用位置

	maxNodeId = -1 ^ (-1 << NodeIdBits) //节点 ID 最大范围

	nodeIdShift        = sequenceBits //左移次数
	timestampLeftShift = sequenceBits + sequenceBits
	sequenceMask       = -1 ^ (-1 << sequenceBits)
)

type IdGenerator struct {
	sync.Mutex

	nodeId   int64
	lastTs   int64
	sequence int64
}

func NewIdGenerator(nodeId int64) (*IdGenerator, error) {
	if nodeId > maxNodeId || nodeId < 0 {
		return nil, fmt.Errorf("invalid node id")
	}
	return &IdGenerator{
		nodeId:   nodeId,
		lastTs:   0,
		sequence: 0,
	}, nil
}
func (g *IdGenerator) GetId() (int64, error) {
	g.Lock()
	defer g.Unlock()
	ts := g.genTime()

	// 相同时间段内，逐步累加sequence
	if ts == g.lastTs {
		g.sequence = (g.sequence + 1) & sequenceMask
		if g.sequence == 0 {
			ts = g.genTimeFront()
		}
	} else {
		g.sequence = 0
	}

	// 防止时间回拨，ts必须大于等于lastTs
	if ts < g.lastTs {
		return 0, fmt.Errorf("clock move backwards, fail to gen id")
	}

	g.lastTs = ts
	id := (ts-twepoch)<<timestampLeftShift | g.nodeId<<nodeIdShift | g.sequence
	return id, nil
}
func ParseId(id int64) (ts time.Time, nodeId int64, sequence int64) {
	timestamp := (id >> timestampLeftShift) + twepoch
	ts = time.Unix(timestamp, 0)
	nodeId = (id >> nodeIdShift) & maxNodeId
	sequence = id & sequenceMask
	return
}

//=====================================internal functions=====================================

// 秒级
func (g *IdGenerator) genTime() int64 {
	return time.Now().Unix()
}

// 秒级
func (g *IdGenerator) genTimeFront() int64 {
	ts := time.Now().Unix()
	for {
		if ts <= g.lastTs {
			time.Sleep(time.Millisecond * 10)
			ts = time.Now().Unix()
		} else {
			break
		}
	}

	return ts
}
func main() {
	idGenerator, err := NewIdGenerator(63)
	if err != nil {
		panic(err)
	}
	idGenerator.sequence = 0
	for i := 0; i < 10; i++ {
		idGenerator.lastTs = time.Now().Unix()
		id, err := idGenerator.GetId()
		if err != nil {
			panic(err)
		}
		fmt.Println(id)
		// parse
		ts, nodeId, sequence := ParseId(id)
		fmt.Printf("ts: %v, nodeId: %v, sequence: %v \n", ts, nodeId, sequence)
	}

}
